//
//  ACCache.m
//  ACCache_Example
//
//  Created by autohome on 2021/8/7.
//  Copyright © 2021 CaiXiang. All rights reserved.
//

#import "ACCache.h"

@interface ACCache()

@property (nonatomic, strong, nullable) ACCacheConfiguration *config;
@property (nonatomic, strong, nullable) dispatch_queue_t ioQueue;

@end

@implementation ACCache

+ (instancetype)cacheWithConfiguration:(ACCacheConfiguration *)config {
    return [[self alloc] initWithCacheConfiguration:config];
}

- (instancetype)initWithCacheConfiguration:(ACCacheConfiguration *)config {
    if (!config) return nil;
    if (config.pluginName.length == 0) return nil;
    if (config.name.length == 0) return nil;
    _config = config;
    return [self initWithName:[NSString stringWithFormat:@"%@/%@",config.pluginName,config.name]];
}

- (instancetype)initWithName:(NSString *)name {
    if (name.length == 0) return nil;
    
    NSString *cacheFolder = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
    NSString *path = [cacheFolder stringByAppendingPathComponent:name];
    
    NSFileManager *manager = [NSFileManager defaultManager];
    BOOL isDir = YES;
    if (![manager fileExistsAtPath:path isDirectory:&isDir]){
        [manager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
    }
    
    return [self initWithPath:path];
}

- (instancetype)initWithPath:(NSString *)path {
    if (path.length == 0) return nil;
    ACDiskCache *diskCache = [[ACDiskCache alloc] initWithPath:path];
    if (!diskCache) return nil;
    diskCache.effectiveCacheTime = _config.effectiveCacheTime;
    
    NSString *name = [path lastPathComponent];
    
    ACMemoryCache *memoryCache = [[ACMemoryCache alloc] initWithName:name];
    memoryCache.shouldRemoveAllObjectsOnMemoryWarning = _config.shouldRemoveAllObjectsOnMemoryWarning;
    memoryCache.shouldRemoveAllObjectsWhenEnteringBackground = _config.shouldRemoveAllObjectsWhenEnteringBackground;
    __weak typeof(self) _self = self;
    memoryCache.didEnterBackgroundBlock = ^(ACMemoryCache * _Nonnull mc) {
        __strong typeof(_self) self = _self;
        //不需要移除内存缓存
        if (!mc.shouldRemoveAllObjectsWhenEnteringBackground) {
            return;
        }
        NSArray *values = mc.allValues.copy;
        NSArray *keys = mc.allkeys.copy;
        if (values.count == 0 || keys == 0) {
            return;
        }
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
            return;
        }
        UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
        __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            //通过标记你的位置来清理任何未完成的任务
            //完全停止或结束任务
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];
        //启动长时间运行的任务并立即返回
        [self saveObjects:values forKeys:keys completionBlock:^{
            NSLog(@"didEnterBackgroundBlock 保存数据成功");
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];
        /*
        NSArray *values = mc.allValues.copy;
        NSArray *keys = mc.allkeys.copy;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [diskCache setObjects:values forKeys:keys];
        });
         */
    };
    
    memoryCache.didReceiveMemoryWarningBlock = ^(ACMemoryCache * _Nonnull mc) {
        __strong typeof(_self) self = _self;
        //不需要移除内存缓存
        if (!mc.shouldRemoveAllObjectsOnMemoryWarning) {
            return;
        }
        NSArray *values = mc.allValues.copy;
        NSArray *keys = mc.allkeys.copy;
        if (values.count == 0 || keys == 0) {
            return;
        }
        Class UIApplicationClass = NSClassFromString(@"UIApplication");
        if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
            return;
        }
        UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
        __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
            //通过标记你的位置来清理任何未完成的任务
            //完全停止或结束任务
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];
        //启动长时间运行的任务并立即返回
        [self saveObjects:values forKeys:keys completionBlock:^{
            NSLog(@"didEnterBackgroundBlock 保存数据成功");
            [application endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];
    };
    
    self = [super init];
    if (self) {
        _diskCache = diskCache;
        _memoryCache = memoryCache;
        _ioQueue = dispatch_queue_create("com.hackemist.SDImageCache", DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)saveObjects:(NSArray<id<NSCoding>>*)objects forKeys:(NSArray <NSString *>*)keys completionBlock:(nullable dispatch_block_t)completionBlock {
        dispatch_async(self.ioQueue, ^{
            [self.diskCache setObjects:objects forKeys:keys];
            if (completionBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    completionBlock();
                });
            }
        });
    }

@end

@implementation ACCache (ACExtension)

- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key {
    [self setObject:object forKey:key toDisk:NO];
}

- (void)setObject:(nullable id<NSCoding>)object forKey:(NSString *)key toDisk:(BOOL)yesOrNo {
    if (!key) return;
    if (_config.cacheType == ACCachePolicyDisk) {
        [_diskCache setObject:object forKey:key];
    } else {
        [_memoryCache setObject:object forKey:key];
        if (yesOrNo) {
            [_diskCache setObject:object forKey:key];
        }
    }
}

- (void)setObject:(nullable id<NSCoding>)object vaule:(nullable id<NSCoding>)vaule forKey:(NSString *)key {
    [self setObject:object vaule:vaule forKey:key toDisk:NO];
}

- (void)setObject:(nullable id<NSCoding>)object vaule:(nullable id<NSCoding>)vaule forKey:(NSString *)key toDisk:(BOOL)yesOrNo {
    if (!key) return;
    NSDictionary *parm = @{@"object":object,@"vaule":vaule};
    if (_config.cacheType == ACCachePolicyDisk) {
        [_diskCache setObject:parm forKey:key];
    } else {
        [_memoryCache setObject:parm forKey:key];
        if (yesOrNo) {
            [_diskCache setObject:parm forKey:key];
        }
    }
}

@end

@implementation ACCache (Extension)

- (BOOL)containsObjectForKey:(NSString *)key {
    BOOL conteains = NO;
    switch (_config.cacheType) {
        case ACCachePolicyMemory:
            conteains = [_memoryCache containsObjectForKey:key];
            break;
        case ACCachePolicyDisk:
            conteains = [_diskCache containsObjectForKey:key];
            break;
        case ACCachePolicyAll:
            conteains = [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
            break;
    }
    return conteains;
}

- (nullable id)objectForKey:(NSString *)key {
    id<NSCoding> object = [_memoryCache objectForKey:key];
    if (!object) {
        object = [_diskCache objectForKey:key];
        if (object && (_config.cacheType == (ACCachePolicyMemory | ACCachePolicyAll))) {
            [_memoryCache setObject:object forKey:key];
        }
    }
    return object;
}

- (void)removeObjectForKey:(NSString *)key {
    [_memoryCache removeObjectForKey:key];
    [_diskCache removeObjectForKey:key];
}

- (void)removeAllObjects {
    [_memoryCache removeAllObjects];
    [_diskCache removeAllObjects];
}

- (NSString *)description {
    if (_config.name) return [NSString stringWithFormat:@"<%@: %p> (%@)", self.class, self, _config.name];
    else return [NSString stringWithFormat:@"<%@: %p>", self.class, self];
}

@end
